<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, minimal-ui" />
        
        <title>Table Design</title>
        
        <script src="../../js/greyspots.js" type="text/javascript"></script>
        <link href="../../css/greyspots.css" type="text/css" rel="stylesheet" />
        
        <link href="../../css/postage.css" type="text/css" rel="stylesheet" />
        
        <script src="../resources/ws-get-functions.js" type="text/javascript"></script>
        
        <!-- right now these scripts are hard coded for postgres 9.2 -->
        <script src="../resources/pg-9.2-tree-functions.js" type="text/javascript"></script>
        <script src="../resources/pg-9.2-tree-queries.js" type="text/javascript"></script>
        
        <script src="../../js/page-zoom.js" type="text/javascript"></script>

        <script>
            var strTableName, strSchemaName, strHasOids, strConstraints, arrConstraint, strGrantsEtc, arrColumn = [], strRelOptions;
            
            scriptQuery.ruleSql = ml(function () {/*
                   SELECT   E'-- DROP RULE ' || quote_ident(pg_rewrite.rulename) || ' ON ' || quote_ident(pg_namespace.nspname) || '.' || quote_ident(pg_class.relname) || E';\n' ||
                            E'\nCREATE OR REPLACE ' || substring(pg_get_ruledef(pg_rewrite.oid, true), 8) ||
                            E'\n\n'
                     FROM pg_class
                LEFT JOIN pg_rewrite ON pg_class.oid = pg_rewrite.ev_class
                LEFT JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                    WHERE pg_rewrite.oid = {{INTOID}};
                * /});
            
            scriptQuery.triggerSql = ml(function () {/*
                   SELECT   '-- Trigger: ' || quote_ident(pg_trigger.tgname) || ' ON ' || quote_ident(nspname) || '.' || quote_ident(relname) || E';\n\n' || 
                        	'-- DROP TRIGGER ' || quote_ident(pg_trigger.tgname) || ' ON ' || quote_ident(nspname) || '.' || quote_ident(relname) || E';\n\n' ||
                        	regexp_replace(regexp_replace(regexp_replace(regexp_replace(pg_get_triggerdef(pg_trigger.oid, true),
                        	' BEFORE ', E'\n   BEFORE '), ' ON ', E'\n   ON '), ' FOR ', E'\n   FOR '), ' EXECUTE ', E'\n   EXECUTE ') || E';\n\n'
                    FROM pg_class 
                    JOIN pg_trigger ON pg_trigger.tgrelid = pg_class.oid
                    JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                   WHERE pg_trigger.oid = {{INTOID}};
                */});
            
            listQuery.tableData = ml(function(){/*
                     -- non-CHECK CONSTRAINTs
                     SELECT 'CONSTRAINT ' || pg_constraint.conname || ' ' || pg_get_constraintdef(pg_constraint.oid, true)
                       FROM pg_class
                       JOIN pg_index ON pg_class.oid = pg_index.indrelid
                       JOIN pg_class clidx ON clidx.oid = pg_index.indexrelid
                  LEFT JOIN pg_constraint ON pg_constraint.conindid = clidx.oid
                      WHERE pg_constraint.oid IS NOT NULL AND pg_class.oid = {{INTOID}}
                     UNION ALL
                     -- CHECK CONSTRAINTs
                     SELECT 'CONSTRAINT ' || conname || ' ' || pg_get_constraintdef(pg_constraint.oid, true)
                       FROM pg_constraint
                       JOIN pg_class pg_class ON pg_class.oid = conrelid
                       JOIN pg_namespace nl ON nl.oid = relnamespace
            LEFT OUTER JOIN pg_description des ON des.objoid = pg_constraint.oid
                      WHERE contype = 'c' AND pg_class.oid = {{INTOID}};
                    
                    SELECT COALESCE((SELECT E'\n\n' || (SELECT array_to_string(array_agg( 'GRANT ' || 
                    	(SELECT array_to_string((SELECT array_agg(perms ORDER BY srt)
                    	FROM (	SELECT 1 as srt, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'r($|[^*])' THEN 'SELECT' END as perms
                    		UNION SELECT 2, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'w($|[^*])' THEN 'UPDATE' END
                    		UNION SELECT 3, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'a($|[^*])' THEN 'INSERT' END
                    		UNION SELECT 4, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'd($|[^*])' THEN 'DELETE' END
                    		UNION SELECT 5, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'D($|[^*])' THEN 'TRUNCATE' END
                    		UNION SELECT 6, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'x($|[^*])' THEN 'REFERENCES' END
                    		UNION SELECT 7, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 't($|[^*])' THEN 'TRIGGER' END ) em
                    		WHERE perms is not null),',')) ||
                    	' ON TABLE ' || quote_ident(pg_namespace.nspname) || '.' || quote_ident(pg_class.relname) || ' TO ' ||
                    	CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[1] = '' THEN 'public' ELSE (regexp_split_to_array(unnest::text,'[=/]'))[1] END || 
                    	';' ), E'\n')
                    	FROM unnest(relacl) 
                    	WHERE (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ '(r|w|a|d|D|x|t)($|[^*])' 
                    	)
                    FROM pg_class 
                    JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                    WHERE pg_class.oid = {{INTOID}} ),'')
                    
                    ||  COALESCE((SELECT E'\n\n' || (SELECT array_to_string(array_agg( 'GRANT ' || 
                    	(SELECT array_to_string((SELECT array_agg(perms ORDER BY srt)
                    	FROM (	SELECT 1 as srt, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'r\*' THEN 'SELECT' END as perms
                    		UNION SELECT 2, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'w\*' THEN 'UPDATE' END
                    		UNION SELECT 3, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'a\*' THEN 'INSERT' END
                    		UNION SELECT 4, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'd\*' THEN 'DELETE' END
                    		UNION SELECT 5, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'D\*' THEN 'TRUNCATE' END
                    		UNION SELECT 6, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 'x\*' THEN 'REFERENCES' END
                    		UNION SELECT 7, CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ 't\*' THEN 'TRIGGER' END ) em
                    		WHERE perms is not null),',')) ||
                    	' ON TABLE ' || quote_ident(pg_namespace.nspname) || '.' || quote_ident(pg_class.relname) || ' TO ' ||
                    	CASE WHEN (regexp_split_to_array(unnest::text,'[=/]'))[1] = '' THEN 'public' ELSE quote_ident((regexp_split_to_array(unnest::text,'[=/]'))[1]) END || 
                    	' WITH GRANT OPTION;'), E'\n')
                    	FROM unnest(relacl) 
                    	WHERE (regexp_split_to_array(unnest::text,'[=/]'))[2] ~ '(r|w|a|d|D|x|t)\*' )
                    FROM pg_class 
                    JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                    WHERE pg_class.oid = {{INTOID}} ), '')
                    
                    || COALESCE(
                    (SELECT E'\n\n' || array_to_string((SELECT array_agg(ok.perms || E'\n') FROM (SELECT 'GRANT ' || (SELECT array_to_string((SELECT array_agg(perms)
                    	FROM (	SELECT 4, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'r[^*]' THEN 'SELECT(' || att.attname || ')' END as perms
                    		UNION SELECT 3, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'w[^*]' THEN 'UPDATE(' || att.attname || ')' END
                    		UNION SELECT 2, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'a[^*]' THEN 'INSERT(' || att.attname || ')' END
                    		UNION SELECT 1, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'x[^*]' THEN 'REFERENCES(' || att.attname || ')' END ) em
                    		WHERE perms is not null
                    		ORDER BY 1),','
                    		)) ||
                    	' ON ' || quote_ident(pg_namespace.nspname) || '.' || quote_ident(pg_class.relname) || 
                    	' TO ' || CASE WHEN (regexp_split_to_array(att.attacl::text,'[=/]'))[1] = '' THEN 'public' ELSE quote_ident((regexp_split_to_array(att.attacl::text,'[=/]'))[1]) END ||
                    	';' as perms
                    FROM pg_class 
                    LEFT JOIN pg_attribute att ON att.attrelid = pg_class.oid 
                    JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                    WHERE pg_class.oid = {{INTOID}} AND (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'r[^*]|w[^*]|a[^*]|x[^*]')ok),'')), '')
                    
                    || COALESCE((SELECT E'\n' || array_to_string((SELECT array_agg(ok.perms || E'\n') FROM (SELECT 'GRANT ' || (SELECT array_to_string((SELECT array_agg(perms)
                    	FROM (	SELECT 4, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'r\*' THEN 'SELECT(' || att.attname || ')' END as perms
                    		UNION SELECT 3, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'w\*' THEN 'UPDATE(' || att.attname || ')' END
                    		UNION SELECT 2, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'a\*' THEN 'INSERT(' || att.attname || ')' END
                    		UNION SELECT 1, CASE WHEN (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ 'x\*' THEN 'REFERENCES(' || att.attname || ')' END ) em
                    		WHERE perms is not null
                    		ORDER BY 1),','
                    		)) ||
                    	' ON ' || quote_ident(pg_namespace.nspname) || '.' || quote_ident(pg_class.relname) || 
                    	' TO ' || CASE WHEN (regexp_split_to_array(att.attacl::text,'[=/]'))[1] = '' THEN 'public' ELSE quote_ident((regexp_split_to_array(att.attacl::text,'[=/]'))[1]) END  ||
                    	' WITH GRANT OPTION;' as perms
                    FROM pg_class 
                    LEFT JOIN pg_attribute att ON att.attrelid = pg_class.oid 
                    JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                    WHERE pg_class.oid = {{INTOID}} AND (regexp_split_to_array((att.attacl)::text, '[=/]'))[2] ~ '(r|w|a|x)\*')ok),'')), '')
                    
                    
                    || COALESCE((SELECT E'\n\n' || array_to_string((SELECT array_agg(ok.perms || E'\n') FROM (
                    SELECT E'-- Index: ' || quote_ident(nsp.nspname) || '.' || quote_ident(clidx.relname) || 
                    	E'\n\n-- DROP INDEX ' || quote_ident(nsp.nspname) || '.' || quote_ident(clidx.relname) || 
                    	E';\n\n' ||
                    	regexp_replace(regexp_replace(pg_get_indexdef(clidx.oid),' ON ', E'\n   ON '),
                    	' USING ', E'\n   USING ') || E';\n\n' as perms
                    FROM pg_class cl 
                    JOIN pg_index idx ON cl.oid=idx.indrelid 
                    JOIN pg_class clidx ON clidx.oid=idx.indexrelid 
                    LEFT JOIN pg_namespace nsp ON nsp.oid = cl.relnamespace 
                    WHERE cl.oid = {{INTOID}} AND (SELECT count(*) FROM pg_constraint con WHERE con.conindid = clidx.oid) = 0
                    )ok),'')), '');
                */});
                
            
            function getDump(intSchemaOid) {
                var strQuery, handleListResults, strTableSuffix = Math.random().toString().substring(2);
                
                // build query for getting all of the lists of objects
                strQuery = '';
                
                strQuery +=
                    '\n\n' +
                    // operators are included because of functions
                    'SELECT oid, name, schema_name, \'operator\' AS objType, 1 AS srt FROM (' +
                        listQuery.operators.replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'UNION ALL ' +
                    // aggregates are included because of functions
                    'SELECT oid, name, schema_name, \'aggregate\' AS objType, 2 AS srt FROM (' +
                        listQuery.aggregates.replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'UNION ALL ' +
                    // functions are included so that the user can do a find and replace on a table name or a column name
                    'SELECT oid, name, schema_name, \'function\' AS objType, 3 AS srt FROM (' +
                        listQuery.functions.replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'UNION ALL ' +
                    'SELECT oid, name, schema_name, \'function\' AS objType, 4 AS srt FROM (' +
                        listQuery.triggers.replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'UNION ALL ' +
                    'SELECT oid, name, schema_name, \'view\' AS objType, 5 AS srt FROM (' +
                        listQuery.views.replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'UNION ALL ' +
                    'SELECT oid, name, schema_name, \'trigger\' AS objType, 6 AS srt FROM (' +
                            ml(function() {/*
                                  SELECT pg_trigger.oid,
                                         quote_ident(tgname) || ' ON ' || quote_ident(nspname) || '.' || quote_ident(relname) AS name,
                                         nspname AS schema_name
                                    FROM pg_class 
                                    JOIN pg_trigger ON pg_trigger.tgrelid = pg_class.oid
                                    JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                                   WHERE pg_namespace.oid = {{INTOID}}
                                     AND pg_trigger.oid IS NOT NULL
                                     AND pg_trigger.tgisinternal != TRUE
                                     AND pg_class.relkind = 'r';
                            */}).replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'UNION ALL ' +
                    'SELECT oid, name, schema_name, \'rule\' AS objType, 7 AS srt FROM (' +
                            ml(function() {/*
                                  SELECT pg_rewrite.oid,
                                         quote_ident(rulename) || ' ON ' || quote_ident(nspname) || '.' || quote_ident(relname) AS name,
                                         nspname AS schema_name
                                    FROM pg_class
                               LEFT JOIN pg_rewrite ON pg_rewrite.ev_class = pg_class.oid
                               LEFT JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                                   WHERE pg_namespace.oid = {{INTOID}} AND pg_rewrite.oid IS NOT NULL AND pg_class.relkind = 'r';
                            */}).replace(/\{\{INTOID\}\}/gim, intSchemaOid).replace(';', '') +
                    ') em' +
                    '\n\n' +
                    'ORDER BY srt, 1';
                
                // function to handle the query results
                handleListResults = function (arrResult) {
                    var strDumpQuery = '', strQuery, i, len, tempFunction, handleScriptResults, arrRow;
                    
                    //console.log(arrResult);
                    
                    for (i = arrResult.length - 1; i >= 0; i -= 1) {
                        strDumpQuery +=
                            'DROP ' + arrResult[i][3].toUpperCase() + ' ' +
                                (
                                    arrResult[i][3].toUpperCase() === 'TRIGGER' ||
                                    arrResult[i][3].toUpperCase() === 'RULE'
                                        ? ''
                                        : strSchemaName + '.'
                                ) + arrResult[i][1] + ';\n';
                    }
                    
                    strDumpQuery += '\nALTER TABLE ' +
                                        strSchemaName + '.' + strTableName +
                                        ' RENAME TO ' + strTableName + strTableSuffix + ';\n';
                    
                    arrConstraint = strConstraints.split('\n');
                    //console.log(strConstraints, arrConstraint);
                    
                    var intEndConstraintName;
                    for (i = 0, len = arrConstraint.length; i < len; i += 1) {
                        if (arrConstraint[i].trim()) {
                            intEndConstraintName = arrConstraint[i].indexOf(' ');
                            intEndConstraintName = intEndConstraintName + arrConstraint[i].substring(intEndConstraintName + 1).indexOf(' ') + 1;
                            strDumpQuery += '\nALTER TABLE ';
                            strDumpQuery += strSchemaName;
                            strDumpQuery += '.';
                            strDumpQuery += strTableName;
                            strDumpQuery += strTableSuffix;
                            strDumpQuery += ' DROP ';
                            strDumpQuery += arrConstraint[i].substring(1, intEndConstraintName);
                            strDumpQuery += ';';
                        }
                    }
                    
                    strDumpQuery += '\n\nCREATE TABLE ';
                    strDumpQuery += strSchemaName;
                    strDumpQuery += '.';
                    strDumpQuery += strTableName;
                    strDumpQuery += ' (\n';
                    arrRow = document.querySelectorAll('tr');
                    for (i = 1, len = arrRow.length - 1; i < len; i += 1) {
                        strDumpQuery += '\t';
                        strDumpQuery += arrRow[i].children[2].children[0].value;
                        strDumpQuery += ' ';
                        strDumpQuery += arrRow[i].children[3].children[0].value;
                        strDumpQuery += arrRow[i].children[4].children[0].value === 'true' ? ' NOT NULL' : '';
                        strDumpQuery += arrRow[i].children[5].children[0].value !== '' ? ' DEFAULT ' : '';
                        strDumpQuery += arrRow[i].children[5].children[0].value;
                        
                        strDumpQuery += arrRow[i].children[6].children[0].value !== '' &&
                                        arrRow[i].children[6].children[0].value !== '0' &&
                                        arrRow[i].children[6].children[0].value !== 'default' ? ' COLLATE "' : '';
                        
                        strDumpQuery += arrRow[i].children[6].children[0].value !== '' &&
                                        arrRow[i].children[6].children[0].value !== '0' &&
                                        arrRow[i].children[6].children[0].value !== 'default' ? arrRow[i].children[6].children[0].value : '';
                        
                        strDumpQuery += arrRow[i].children[6].children[0].value !== '' &&
                                        arrRow[i].children[6].children[0].value !== '0' &&
                                        arrRow[i].children[6].children[0].value !== 'default' ? '"' : '';
                        strDumpQuery += (i + 1) < len || strConstraints.trim() !== '' ? ',\n' : '\n';
                    }
                    strDumpQuery += strConstraints;
                    strDumpQuery += ')\n';
                    strDumpQuery += 'WITH (\n';
                    strDumpQuery += '\tOIDS=';
                    strDumpQuery += strHasOids;
                    strDumpQuery += strRelOptions !== '' ? ',\n\t' : '';
                    strDumpQuery += strRelOptions;
                    strDumpQuery += '\n';
                    strDumpQuery += ');\n';
                    
                    strDumpQuery += 'INSERT INTO ';
                    strDumpQuery += strSchemaName;
                    strDumpQuery += '.';
                    strDumpQuery += strTableName;
                    strDumpQuery += ' (';
                    for (i = 0, len = arrColumn.length; i < len; i += 1) {
                        if (arrColumn[i].original) {
                            strDumpQuery += arrColumn[i].updated.name;
                            strDumpQuery += (i + 1) < len ? ', ' : '';
                        } else {
                            arrColumn.splice(i, 1);
                            i -= 1;
                            len -= 1;
                        }
                    }
                    strDumpQuery += ')\nSELECT ';
                    for (i = 0, len = arrColumn.length; i < len; i += 1) {
                        if (arrColumn[i].original) {
                            strDumpQuery += arrColumn[i].original.name;
                            strDumpQuery += '::';
                            strDumpQuery += arrColumn[i].updated.type;
                            strDumpQuery += ' AS ';
                            strDumpQuery += arrColumn[i].updated.name;
                            strDumpQuery += (i + 1) < len ? ', ' : '\n';
                        }
                    }
                    strDumpQuery += 'FROM ';
                    strDumpQuery += strSchemaName;
                    strDumpQuery += '.';
                    strDumpQuery += strTableName;
                    strDumpQuery += strTableSuffix;
                    strDumpQuery += ';\n\n';
                    strDumpQuery += 'DROP TABLE ';
                    strDumpQuery += strSchemaName;
                    strDumpQuery += '.';
                    strDumpQuery += strTableName;
                    strDumpQuery += strTableSuffix;
                    strDumpQuery += ';\n\n';
                    
                    //console.log(strDumpQuery);
                    
                    // load queries for listed objects
                    strQuery = '';
                    var j = 0, len1 = arrResult.length;
                    for (i = 0, len = arrResult.length; i < len; i += 1) {
                        
                        //console.log('arrResult ' + i + ':', arrResult[i]);
                        //console.log('object' + GS.strToTitle(arrResult[i][3]));
                        //console.log('arrResult 0:', arrResult[i][0]);
                        //console.log('arrResult 1:', arrResult[i][1]);
                        //console.log('arrResult 2:', arrResult[i][2]);
                        //console.log('arrResult 3:', arrResult[i][3]);
                        //console.log('object ' + i + ':', arrResult[i][2] + '.' + arrResult[i][1]);
                        //console.log(scriptQuery['object' + GS.strToTitle(arrResult[i][3])]);
                        strQuery += '\n\n' +
                            (
                                //scriptQuery[arrResult[i][3].toLowerCase()] ||
                                //scriptQuery[arrResult[i][3].toLowerCase() + 'Sql']
                                scriptQuery['object' + GS.strToTitle(arrResult[i][3])]
                            )
                                .replace(/\{\{INTOID\}\}/gim, arrResult[i][0])
                                .replace(/\{\{STRSQLSAFENAME\}\}/gim, quote_ident(strSchemaName) + '.' + quote_ident(arrResult[i][1]));
                    }
                    
                    handleScriptResults = function (arrResult) {
                        var i, len;
                        if (arrResult.length !== 2) {
                            console.error(arrResult.length + ' results for query ' + j + ' of ' + len1, arrResult[0], arrResult[1]);
                            GS.msgbox(arrResult.length + ' results for query ' + j + ' of ' + len1, arrResult[0], arrResult[1]);
                        }
                        
                        // removes column name record
                        arrResult.splice(0, 1);
                        
                        //console.log('arrResult: ' + arrResult);
                        //console.log(j, len1);
                        
                        for (i = 0, len = arrResult.length; i < len; i += 1) {
                            strDumpQuery += arrResult[i][0] + '\n\n'; //GS.decodeFromTabDelimited(arrResult[i][0]) + '\n\n';
                        }
                        j += len;
                        
                        if (j === len1) {
                            GS.removeLoader('generate-script');
                            strDumpQuery += strGrantsEtc;
                            //console.log(strDumpQuery);
                            window.top.newTab('sql', strSchemaName + '.' + strTableName, {'strContent': strDumpQuery});
                        }
                    };
                    
                    if (strQuery) {
                        //console.log('strQuery: ' + strQuery);
                        getListsForDump(strQuery, handleScriptResults);
                    } else {
                        handleScriptResults([]);
                    }
                };
                
                // load lists of objects to download
                if (strQuery) {
                    getListsForDump(strQuery, function (arrResults) {
                        arrResults.splice(0, 1);
                        
                        handleListResults(arrResults);
                    });
                } else {
                    handleListResults({});
                }
            }
            
            function initTableDesign(arrColumns, strColumnData) {
                'use strict';
                var i, len, col_i, col_len, arrColumns, arrColumnTypes, arrRecords, arrRecord, recordHTML, strRecords, sortColumnHandler;
                
                //console.log(strTableName, strColumnData);
                
                // if last character is a \n: remove it
                if (strColumnData[strColumnData.length - 1] === '\n') {
                    strColumnData = strColumnData.substring(0, strColumnData.length - 1);
                }
                
                // split records
                arrRecords = strColumnData.split('\n');
                
                //console.log(arrColumns, arrColumnTypes, arrRecords);
                
                // build json object
                for (i = 0, len = arrRecords.length; i < len; i += 1) {
                    arrRecord = arrRecords[i].split('\t');
                    
                    arrRecords[i] = {};
                    arrRecords[i].grid_record_number = i + 1;
                    
                    for (col_i = 0, col_len = arrRecord.length; col_i < col_len; col_i += 1) {
                        arrRecords[i][arrColumns[col_i]] = GS.decodeFromTabDelimited(arrRecord[col_i]);
                    }
                    
                    arrColumn.push({
                        original: {
                            name: arrRecords[i].attname
                        },
                        updated: {
                            name: arrRecords[i].attname,
                            type: arrRecords[i].format_type
                        }
                    });
                }
                
                //console.log(arrRecords);
                
                //attname\tformat_type\tcol_not_null\tcol_default\tcoll_collation
                recordHTML = ml(function () {/*
                    <th>{{! row.grid_record_number }}</th>
                    <td><gs-button mini icononly class="drag-handle" icon="bars"></gs-button></td>
                    <td><gs-text mini column="attname" value="{{! row['attname'] }}" placeholder="Column Name" data-value="{{! row['attname'] }}"/></td>
                    <td><gs-text mini column="format_type" value="{{! row['format_type'] }}" placeholder="Type" data-value="{{! row['format_type'] }}"/></td>
                    <td><gs-checkbox mini column="col_not_null" value="{{= row['col_not_null'] === "-1" }}" data-value="{{= row['col_not_null'] === "-1" }}"/></td>
                    <td><gs-text mini column="col_default" value="{{! row['col_default'] }}" placeholder="Default" data-value="{{! row['col_default'] }}"/></td>
                    <td><gs-text mini column="col_collation" value="{{! row['col_collation'] === '0' ? '' : row['col_collation'] }}" placeholder="Collation"/></td>
                */});
                
                // build tbody contents
                strRecords = doT.template(
                                    '{{ var row, i, len, qs = GS.qryToJSON(GS.getQueryString());\n' +
                                    '\n' +
                                    'for (i = 0, len = jo.length; i < len; i += 1) {\n' +
                                    '    row = jo[i]; }}\n' +
                                    '    <tr>' + recordHTML + '</tr>\n' +
                                    '{{ } }}')(arrRecords);
                strRecords += '<tr id="column-insert">';
                strRecords += '<th>&gt;</th>';
                strRecords += '<td></td>';
                strRecords += '<td><gs-text mini        column="attname"        placeholder="Column Name"/></td>';
                strRecords += '<td><gs-text mini        column="format_type"    placeholder="Type"/></td>';
                strRecords += '<td><gs-checkbox mini    column="col_not_null"   value="0"/></td>';
                strRecords += '<td><gs-text mini        column="col_default"    placeholder="Default"/></td>';
                strRecords += '<td><gs-text mini        column="col_collation"  placeholder="Collation"/></td>';
                strRecords += '</tr>';
                
                //console.log(strRecords);
                
                document.getElementById('div-column-list-container').innerHTML =
                    '<table>' +
                        '<thead>' +
                            '<th style="width: 3em;">#</th>' +
                            '<th style="width: 2em;"></th>' +
                            '<th>Name</th>' +
                            '<th style="width: 14em;">Type</th>' +
                            '<th style="width: 3em;">NOT NULL?</th>' +
                            '<th>Default</th>' +
                            '<th>Collation</th>' +
                        '</thead>' +
                        '<tbody>' +
                            strRecords +
                        '</tbody>' +
                    '</table>';
                
                document.getElementById('column-insert').addEventListener('keydown', function __________THIS_FUNCTION__________(event) {
                    if (event.keyCode.toString() === GS.keyCode('ENTER')) {
                        var element = event.target,
                            row = GS.findParentTag(element, 'tr'),
                            strColumnInsert = '';
                        
                        element = element.tagName === 'GS-CHECKBOX' ? element : element.parentNode;
                        row.children[0].innerText = (arrRecords.length += 1);
                        row.children[1].innerHTML = '<gs-button mini icononly class="drag-handle" icon="bars"></gs-button>';
                        
                        row.removeAttribute('id');
                        
                        strColumnInsert += '<tr id="column-insert">';
                        strColumnInsert += '<th>&gt;</th>';
                        strColumnInsert += '<td></td>';
                        strColumnInsert += '<td><gs-text mini        column="attname"        placeholder="Column Name" /></td>';
                        strColumnInsert += '<td><gs-text mini        column="format_type"    placeholder="Type" /></td>';
                        strColumnInsert += '<td><gs-checkbox mini    column="col_not_null"   value="0" /></td>';
                        strColumnInsert += '<td><gs-text mini        column="col_default"    placeholder="Default" /></td>';
                        strColumnInsert += '<td><gs-text mini        column="col_collation"  placeholder="Collation" /></td>';
                        strColumnInsert += '</tr>';
                        
                        var temp = document.createElement('table');
                        temp.innerHTML = strColumnInsert;
                        document.querySelector('#div-column-list-container tbody').appendChild(temp.querySelector('tr'));
                        document.querySelector('#column-insert gs-text').firstElementChild.focus();
                        
                        this.removeEventListener('keydown', __________THIS_FUNCTION__________);
                        document.getElementById('column-insert').addEventListener('keydown', __________THIS_FUNCTION__________);
                        
                        arrColumn.push({
                            updated: {
                                name: arrRecords[i].attname,
                                type: arrRecords[i].format_type
                            }
                        });
                    }
                });
                
                document.getElementById('div-column-list-container').addEventListener('change', function (event) {
                    if (event.target.tagName === 'GS-CHECKBOX' && event.target.parentNode.parentNode.hasAttribute('id')) {
                        // We are in the insert column row
                        GS.triggerEvent(event.target, 'keydown', {
                            keyCode: parseInt(GS.keyCode('ENTER'), 10)
                        });
                    } else if (event.target.hasAttribute('data-value')) {
                        var row = GS.findParentTag(event.target, 'tr'), i = 0, len = arrColumn.length, originalName = event.target.getAttribute('data-value');
                        for (; i < len; i += 1) {
                            if (arrColumn[i].original && arrColumn[i].original.name === originalName) {
                                arrColumn[i].updated.name = row.children[2].children[0].value;
                                arrColumn[i].updated.type = row.children[3].children[0].value;
                                
                                break;
                            }
                        }
                    }
                });
                
                // create sort handle function
                sortColumnHandler = function (dragParent, event) {
                    var intTop = GS.mousePosition(event).top, matchedElement, bolMatchedLast, i, len,
                        offsetsCache = dragParent.offsetsCache, borderElement = dragParent.dragBorderElement,
                        ghostElement = dragParent.dragGhostElement;
                    
                    ghostElement.style.top = (intTop - (ghostElement.offsetHeight / 2)) + 'px';
                    
                    if (offsetsCache[0].top > intTop) {
                        matchedElement = offsetsCache[0].element;
                        bolMatchedLast = false;
                        
                    } else {
                        bolMatchedLast = false;
                        for (i = 0, len = offsetsCache.length; i < len; i += 1) {
                            if (offsetsCache[i + 1]) {
                                if (offsetsCache[i].top <= intTop &&
                                    offsetsCache[i].top + ((offsetsCache[i + 1].top - offsetsCache[i].top) / 2) > intTop) {
                                    
                                    matchedElement = offsetsCache[i].element;
                                    break;
                                    
                                } else if (offsetsCache[i].top <= intTop &&
                                            offsetsCache[i].top + ((offsetsCache[i + 1].top - offsetsCache[i].top) / 2) <= intTop &&
                                            offsetsCache[i + 1].top > intTop) {
                                    matchedElement = offsetsCache[i + 1].element;
                                    break;
                                }
                            } else {
                                if (offsetsCache[i].top + (offsetsCache[i].height / 2) >= intTop) {
                                    matchedElement = offsetsCache[i].element;
                                    break;
                                    
                                } else if (offsetsCache[i].top + (offsetsCache[i].height / 2) <= intTop) {
                                    matchedElement = offsetsCache[i].element;
                                    bolMatchedLast = true;
                                    break;
                                }
                            }
                        }
                    }
                    
                    //console.log(bolMatchedLast); //matchedElement,
                    
                    if (matchedElement !== dragParent.matchedElement || bolMatchedLast !== dragParent.bolMatchedLast) {
                        if (bolMatchedLast === true) {
                            dragParent.insertBefore(borderElement, dragParent.querySelector('#column-insert'));
                            
                        } else {
                            dragParent.insertBefore(borderElement, matchedElement);
                        }
                        
                        dragParent.matchedElement = matchedElement;
                        dragParent.bolMatchedLast = bolMatchedLast;
                    }
                    
                    //console.log('sort', intTop);//, event, ghostElement);
                };
                
                // bind sort handles
                document.getElementById('div-column-list-container').addEventListener(evt.mousedown, function (event) {
                    var dragParent = event.target.parentNode.parentNode.parentNode, dragLine, mousemoveHandler, mouseupHandler, arrElements, i, len;
                    
                    if (event.target.classList.contains('drag-handle')) {
                        // save drag line
                        dragLine = event.target.parentNode.parentNode;
                        
                        // save a cache of line offsets
                        arrElements = xtag.toArray(dragParent.children);
                        dragParent.offsetsCache = [];
                        // the - 1 is so that the insert record is ignored
                        for (i = 0, len = arrElements.length - 1; i < len; i += 1) {
                            dragParent.offsetsCache.push({
                                'element': arrElements[i],
                                'top': GS.getElementOffset(arrElements[i]).top,
                                'height': arrElements[i].offsetHeight
                            });
                        }
                        //console.log(dragParent.offsetsCache);
                        
                        // create and save ghost
                        dragParent.dragGhostElement = document.createElement('table');
                        dragParent.dragGhostElement.appendChild(dragLine.cloneNode(true));
                        dragParent.dragGhostElement.setAttribute('style', 'position: absolute;' +
                                                                          'left: 0; top: 0;' +
                                                                          'padding: 2px; opacity: 0.6;' +
                                                                          'background-color: #F0F0F0;' +
                                                                          'border: 1px solid #000000;' +
                                                                          'width: ' + dragLine.clientWidth + 'px;');
                        
                        dragParent.appendChild(dragParent.dragGhostElement);
                        
                        // create and save border element
                        dragParent.dragBorderElement = document.createElement('div');
                        dragParent.dragBorderElement.setAttribute('style', 'position:relative;');
                        dragParent.dragBorderElement.innerHTML = '<div style="position: absolute;' +
                                                                              'top: -2px; left: -2px;' +
                                                                              'width: ' + dragLine.clientWidth + 'px; padding: 2px;' +
                                                                              'background-color: #00CC00;"></div>';
                        
                        dragParent.appendChild(dragParent.dragBorderElement);
                        
                        // call sort handler
                        sortColumnHandler(dragParent, event);
                        
                        // bind mousemove and mouseup
                        mousemoveHandler = function (event) {
                            if (event.which === 0 && !evt.touchDevice) {
                                mouseupHandler(event);
                                
                            } else {
                                sortColumnHandler(dragParent, event);
                                event.preventDefault();
                            }
                        };
                        
                        mouseupHandler = function (event) {
                            var arrRowNumber;
                            
                            if (dragParent.bolMatchedLast) {
                                dragParent.insertBefore(dragLine, dragParent.querySelector('#column-insert'));
                            } else {
                                dragParent.insertBefore(dragLine, dragParent.matchedElement);
                            }
                            
                            document.body.removeEventListener(evt.mousemove, mousemoveHandler);
                            document.body.removeEventListener(evt.mouseup, mouseupHandler);
                            dragParent.removeChild(dragParent.dragGhostElement);
                            dragParent.removeChild(dragParent.dragBorderElement);
                            
                            // I am using xtag.query so that I already have an array
                            arrRowNumber = xtag.query(dragParent, 'tbody tr:not(#column-insert) th:first-child');
                            arrRowNumber.forEach(function (curElement, i) {
                                curElement.innerText = (i + 1).toString();
                            });
                        };
                        
                        document.body.addEventListener(evt.mousemove, mousemoveHandler);
                        document.body.addEventListener(evt.mouseup, mouseupHandler);
                    }
                });
            }
            
            function refreshEditor(strOID) {
                var strTableColumns = '', i = 0;
                strConstraints = '';
                strTableName = '';
                
                GS.requestRawFromSocket(GS.envSocket, listQuery.tableData.replace(/\{\{INTOID\}\}/g, strOID), function (data) {
                    //console.log('1', i, data);
                    
                    if (data.intQueryNumber <= 1) {
                        if (data.strMessage !== '\\.') {
                            if (data.intQueryNumber === 0) {
                                strConstraints += data.strMessage;
                            } else {
                                strGrantsEtc = data.strMessage;
                            }
                            
                        } else if (data.strMessage === '\\.' && data.intQueryNumber === 1) {
                            var arrConstraints = strConstraints.split('\n'), len = arrConstraints.length;
                            //console.log(strConstraints);
                            strConstraints = '';
                            arrConstraints.forEach(function (curCon, i) {
                                strConstraints += '\t';
                                strConstraints += GS.decodeFromTabDelimited(curCon);
                                strConstraints += (i + 1) < len ? ',\n' : '\n';
                            });
                            var arrTriggersEtc = strGrantsEtc.split('\n'), len = arrTriggersEtc.length;
                            strGrantsEtc = '';
                            arrTriggersEtc.forEach(function (cur, i) {
                                strGrantsEtc += '\t';
                                strGrantsEtc += GS.decodeFromTabDelimited(cur);
                                strGrantsEtc += (i + 1) < len ? ',\n' : '\n';
                            });
                            
                            GS.requestRawFromSocket(GS.envSocket, ml(function () {/*
                                     SELECT pg_namespace.nspname, relname, relhasoids, array_to_string(reloptions, E',\n\t') AS reloptions
                                       FROM pg_class
                                  LEFT JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
                                      WHERE pg_class.oid = {{INTOID}};
                                */}).replace(/\{\{INTOID\}\}/g, strOID), function (data) {
                                
                                //console.log('2', data);
                                
                                if (data.strMessage !== '\\.' && data.intQueryNumber === 0) {
                                    strTableName += data.strMessage;
                                } else if (data.strMessage === '\\.' && data.intQueryNumber === 0) {
                                    strSchemaName = GS.decodeFromTabDelimited(strTableName.substring(0, strTableName.indexOf('\t')));
                                    strTableName = strTableName.substring(strTableName.indexOf('\t') + 1);
                                    
                                    strHasOids = GS.decodeFromTabDelimited(strTableName.substring(strTableName.indexOf('\t')))[0] === 't' ? 'TRUE' : 'FALSE';
                                    strRelOptions = strTableName.substring(strTableName.indexOf('\t') + 1);
                                    strRelOptions = strRelOptions.substring(strRelOptions.indexOf('\t') + 1);
                                    strRelOptions = GS.decodeFromTabDelimited(strRelOptions.substring(strRelOptions.indexOf('\t')));
                                    
                                    strTableName = GS.decodeFromTabDelimited(strTableName.substring(0, strTableName.indexOf('\t')));
                                    
                                    //console.log(strSchemaName, strTableName, strHasOids, strRelOptions);
                                    
                                    var strColumns = '';
                                    GS.requestRawFromSocket(GS.envSocket, ml(function () {/*
                                         SELECT attname, format_type(atttypid, atttypmod),
                                                (CASE WHEN attnotnull THEN '-1' ELSE '0' END) AS col_not_null,
                                                (CASE WHEN atthasdef THEN pg_catalog.pg_get_expr(adbin, adrelid) ELSE '' END) AS col_default,
                                                (CASE WHEN attcollation <> 0 THEN pg_collation.collname ELSE '0' END) AS col_collation
                                           FROM pg_attribute
                                      LEFT JOIN pg_attrdef ON pg_attrdef.adrelid = pg_attribute.attrelid AND pg_attrdef.adnum = pg_attribute.attnum
                                      LEFT JOIN pg_collation ON pg_collation.oid = pg_attribute.attcollation
                                          WHERE pg_attribute.attisdropped IS FALSE AND pg_attribute.attnum > 0 AND attrelid = {{INTOID}}
                                       ORDER BY attnum ASC*/}).replace(/\{\{INTOID\}\}/g, strOID), function (data) {
                                        
                                        //console.log(data);
                                        
                                        if (data.intQueryNumber === 0 && data.strMessage !== '\\.') {
                                            //console.log(data);
                                            strColumns += (strColumns ? '\n' : '') + data.strMessage;
                                            
                                        } else if (data.intQueryNumber === 0 && data.strMessage === '\\.') {
                                            initTableDesign(data.arrColumnNames, strColumns);
                                        }
                                    });
                                }
                            });
                        }
                    }
                });
            }
            
            document.addEventListener('DOMContentLoaded', function () {
                var strQueryString = GS.getQueryString(),
                    strOID = GS.qryGetVal(strQueryString, 'oid'),
                    templateElement;
                
                //console.log('1***', strOID);
                
                // if there is no oid: ask for one
                if (!strOID) {
                    templateElement = document.createElement('template');
                    templateElement.setAttribute('data-theme', 'error');
                    
                    templateElement.innerHTML = ml(function () {/*
                        <gs-page>
                            <gs-header><center><h3>Missing Parameter</h3></center></gs-header>
                            <gs-body padded>
                                This page needs an object ID to edit. You can manually set one by typing one in and clicking "Go".
                                <br />
                                <label><b>Manual OID:</b></label>
                                <gs-text id="text-page-load-oid"></gs-text>
                            </gs-body>
                            <gs-footer>
                                <gs-button dialogclose listen-for-return>Go</gs-button>
                            </gs-footer>
                        </gs-page>
                    */});
                    
                    GS.openDialog(templateElement, '', function () {
                        window.location.search = '?oid=' + encodeURIComponent(document.getElementById('text-page-load-oid').value);
                    });
                    
                // if there is an oid
                } else {
                    //console.log('2***', strOID);
                    refreshEditor(strOID);
                }
                
                document.getElementById('button-generate-script').addEventListener('click', function (event) {
                    var strSchemaOID = '';
                    GS.addLoader('generate-script', 'Loading...');
                    GS.requestRawFromSocket(GS.envSocket,
                                            'SELECT pg_class.relnamespace FROM pg_class WHERE pg_class.oid = ' + strOID,
                                            function (data, error) {
                        if (!error) {
                            if (data.strMessage !== '\\.' && data.intCallbackNumber === 0) {
                                getDump(data.strMessage);
                            }
                        } else {
                            GS.webSocketErrorDialog(data);
                            GS.removeLoader('generate-script');
                        }
                    });
                });
            });
            
            window.addEventListener('load', function () {
                "use strict";
                zoomStart();
            });
        </script>
        
        <style>
            #div-column-list-container {
                display: block;
                position: relative;
                height: 100%;
                overflow: auto;
            }
            #div-column-list-container table {
                border-left: 1px solid #ddd;
            }
            
            #div-column-list-container table input {
                display: block;
                resize: none;
                width: 100%;
                border: 0 none;
                cursor: text;
                padding: 0 0.1em;
                
                -webkit-box-sizing: border-box;
                -moz-box-sizing: border-box;
                -ms-box-sizing: border-box;
                -o-box-sizing: border-box;
                box-sizing: border-box;
            }
            
            #div-column-list-container th, #div-column-list-container td {
                padding: 0.1em;
                cursor: pointer;
                
                -webkit-touch-callout: none;
                -webkit-user-select: none;
                -khtml-user-select: none;
                -moz-user-select: none;
                -ms-user-select: none;
                -o-user-select: none;
                user-select: none;
            }
            
            #div-column-list-container td gs-checkbox {
                padding-top: 0.5em;
            }
            
            #div-column-list-container thead th {
                cursor: s-resize;
                overflow: hidden;
            }
        </style>
        <script src="../../js/settings.js" type="text/javascript"></script>
        <script>
            if (window.parent) {
                window.parent.addEventListener('resize', function () {
                    if (localStorage && localStorage.customCSS) {
                        refreshCustomCSS(localStorage.customCSS);
                    }
                    /*
                    if (localStorage && localStorage.customCSS && document.getElementById('customCss')) {
                        document.getElementById('customCss').innerHTML = localStorage.customCSS;
                    }
                    */
                });
            }
        </script>
        <style id="customCss"></style>
    </head>
    <body>
        <gs-page>
            <gs-body padded>
                <gs-button id="button-generate-script">Generate Script</gs-button><br />
                <div id="div-column-list-container"></div>
            </gs-body>
        </gs-page>
    </body>
</html>